<?php

namespace app\utils\Service\AliCloud;

use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Client\Exception\ClientException;
use AlibabaCloud\Client\Exception\ServerException;

class OssService
{
    private mixed $config;

    public function __construct()
    {
//        $config = get_addon_config('alioss');
//        $endpoint = explode('.',$config['endpoint']);
//        $endpoint = str_replace('oss-','',$endpoint[0]);
//        $this->config = [
//            'ssl' => false,
//            'endpoint' => $endpoint,
//            'access_id' => $config['accessKeyId'],
//            'access_key' => $config['accessKeySecret'],
//            'bucket' => $config['bucket'],
//            'custom_domain' => $config['bucket'],
//            'role_arn' => $config['role_arn'] ?? '',//'acs:ram::1535699267520577:role/ramosstest',
//            'role_session' => $config['role_session'] ?? '',//'yemu123sts',
//        ];
        $this->config = config('oss_sts');
    }

    public function getToken($dir = 'uploads/')
    {
        $ssl             = $this->config['ssl'] ? 'https' : 'http';//上传域名因存储区域不同而节点不同
        $endpoint        = $this->config['endpoint'] ?? '';//上传域名因存储区域不同而节点不同
        $accessKeyId     = $this->config['access_id'] ?? '';//"你自己在阿里云设置的RAM的accessKeyId"; // 你自己的
        $accessKeySecret = $this->config['access_key'] ?? '';//"你自己在阿里云设置的RAM的accessKeySecret"; // 你自己的
        $bucket          = $this->config['bucket'] ?? ""; // 存储桶
        $custom_domain   = "oss-{$endpoint}.aliyuncs.com"; // domain

        //设置该policy超时时间是10s. 即这个policy过了这个有效时间，将不能访问。
        $expiration = $this->gmt_iso8601(time() + 30);
        //最大文件大小.用户可以自己设置
        $condition = ['content-length-range', 0, 1048576000];
        // 表示用户上传的数据，必须是以$dir开始，不然上传会失败，这一步不是必须项，只是为了安全起见，防止用户通过policy上传到别人的目录。
        $start  = ['starts-with', '$key', $dir];
        $arr    = ['expiration' => $expiration, 'conditions' => [$condition, $start]];
        $policy = base64_encode(json_encode($arr));

        // 签名
        $signature = base64_encode(hash_hmac('sha1', $policy, $accessKeySecret, true));

        return [true, [
            'accessKeyId'     => $accessKeyId,
            'accessKeySecret' => $accessKeySecret,
            'bucket'          => $bucket,
            'region'          => "oss-{$endpoint}",
            'domain'          => "{$bucket}.oss-{$endpoint}.aliyuncs.com",
            'custom_domain'   => $custom_domain,
            'ssl'             => $ssl,
            'signature'       => $signature,
            'policy'          => $policy,
        ]];
    }

    /**
     * 阿里云Sts凭证
     */
    public function getStsToken($dir = 'uploads/')
    {
        $ssl             = $this->config['ssl'] ? 'https' : 'http';//上传域名因存储区域不同而节点不同
        $endpoint        = $this->config['endpoint'] ?? '';//上传域名因存储区域不同而节点不同
        $accessKeyId     = $this->config['access_id'] ?? '';//"你自己在阿里云设置的RAM的accessKeyId"; // 你自己的
        $accessKeySecret = $this->config['access_key'] ?? '';//"你自己在阿里云设置的RAM的accessKeySecret"; // 你自己的
        $roleArn         = $this->config['role_arn'] ?? '';  // 角色访问控制 RoleArn，你在设置的
        $bucket          = $this->config['bucket'] ?? ""; // 临时凭证名称，随意
        $roleSessionName = $this->config['role_session'] ?? "stspanda"; // 临时凭证名称，随意
        $custom_domain   = $this->config['custom_domain'] ?? "oss-{$endpoint}.aliyuncs.com"; // 临时凭证名称，随意

        AlibabaCloud::accessKeyClient($accessKeyId, $accessKeySecret)
            ->regionId('cn-qingdao')
            ->asDefaultClient();

        try {
            $result = AlibabaCloud::rpc()
                ->product('Sts')
                ->scheme('https') // https | http
                ->version('2015-04-01')
                ->action('AssumeRole')
                ->method('POST')
                ->host("sts.{$endpoint}.aliyuncs.com")
                ->options([
                    'query' => [
                        'RegionId'        => "cn-qingdao",
                        'RoleArn'         => $roleArn,
                        'RoleSessionName' => $roleSessionName,
                    ],
                ])
                ->request();

            $res = $result->toArray();
            if (empty($res['Credentials'])) {
                return [false, '获取失败'];
            }

            //设置该policy超时时间是10s. 即这个policy过了这个有效时间，将不能访问。
            $expiration = $this->gmt_iso8601(time() + 30);
            //最大文件大小.用户可以自己设置
            $condition = ['content-length-range', 0, 1048576000];
            // 表示用户上传的数据，必须是以$dir开始，不然上传会失败，这一步不是必须项，只是为了安全起见，防止用户通过policy上传到别人的目录。
            $start  = ['starts-with', '$key', $dir];
            $arr    = ['expiration' => $expiration, 'conditions' => [$condition, $start]];
            $policy = base64_encode(json_encode($arr));

            // 签名
            $signature = base64_encode(hash_hmac('sha1', $policy, $res['Credentials']['AccessKeySecret'], true));

            return [true, [
                'accessKeyId'     => $res['Credentials']['AccessKeyId'],
                'accessKeySecret' => $res['Credentials']['AccessKeySecret'],
                'stsToken'        => $res['Credentials']['SecurityToken'],
                'expiration'      => $res['Credentials']['Expiration'],
                'bucket'          => $bucket,
                'region'          => "oss-{$endpoint}",
                'domain'          => "{$bucket}.oss-{$endpoint}.aliyuncs.com",
                'custom_domain'   => $custom_domain,
                'ssl'             => $ssl,
                'signature'       => $signature,
                'policy'          => $policy,
            ]];
        } catch (ClientException $e) {
            return [false, $e->getErrorMessage()];
        } catch (ServerException $e) {
            $error = $this->xml_parser($e->getErrorMessage());
            return [false, $error];
        }
    }

    private function xml_parser($str)
    {
        $xml_parser = xml_parser_create();
        if (!xml_parse($xml_parser, $str, true)) {
            xml_parser_free($xml_parser);
            return $str;
        } else {
            $res = json_decode(json_encode(simplexml_load_string($str, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
            return $res['Message'] ?? '请求错误';
        }
    }


    /**
     * @param $time
     * @return string
     * @throws \Exception
     */
    private function gmt_iso8601($time)
    {
        $dtStr      = date("c", $time);
        $dateTime   = new \DateTime($dtStr);
        $expiration = $dateTime->format(\DateTime::ISO8601);
        $pos        = strpos($expiration, '+');
        $expiration = substr($expiration, 0, $pos);
        return $expiration . "Z";
    }
}